စကားဦး။ နည်းပညာကျွမ်းကျင်သူမဟုတ်ဘဲ ကိုယ်စိတ်ဝင်စားလို့ ရှာဖွေလေ့လာပြီးလုပ်ထားတာမို့လို့ လိုက်လုပ်မယ်ဆိုရင် အရင်ဆုံးကိုယ်တိုင်ရှာဖွေ့လေ့လာဖို့ အကြံပြုပါတယ်။


ကိုယ် self host လုပ်ဖို့စစေခဲ့တာကတော့ မအလ VPN တွေပိတ်ခဲ့တဲ့အချိန်ကနေစခဲ့တာပါ။ အဲ့နောက်မှာတော့ Mozilla က Pocket ကိုပိတ်လိုက်တာကနေ လိုက်ပြီးတော့၊ အစားထိုးဖို့ရှာရာကနေ Karakeep ကိုတွေ့ပြီး self host လုပ်ဖို့ဆုံးဖြတ်ဖြစ်ခဲ့တာပါ။

ကိုယ့် server ကတော့ အတန်ဆုံး Racknerd plan၊ တစ်နှစ်နေမှ USD 11 ပဲပေးရတဲ့ VPS (1GB Ram + 15GB Storage + one IPv4 address)။ သေသေချာချာလုပ်မယ်ဆိုရင်တော့ ဒီထက်ပိုကောင်းတဲ့ VPS တွေသုံးပါ။ ကိုယ့်အတွက်တော့ တစ်ယောက်ထဲသုံးမှာဆိုတော့ အဆင်ပြေနေသေးတယ်။ Domain အတွက်တော့ z.com ကတော့ တစ်ခုဝယ်ပြီး Cloudflare ကိုရှေ့မှာခံပြီးသုံးပါတယ်။ OS ကတော့ Arch Linux ကိုနေ့စဥ်သုံးနေတာဆိုတော့ ဆာဗာပေါ်မှာလည်း Arch Linux ပဲသုံးလိုက်ပါတယ်။ Software တွေအကုန်ကတော့ container နဲ့ဖြစ်လို့ Rootless Podman ရဲ့ Quadlet သုံးပါတယ်။

လက်ရှိ self host လုပ်နေတဲ့ software တွေကတော့


Karakeep

Karakeep ဆိုတာက Mozilla Pocket လိုပဲ ဆောင်းပါးတွေ၊ လင့်တွေကို နောက်မှဖတ်ဖို့သိမ်းထားလို့ရတဲ့ software ပါ။ ကိုယ်ကြိုက်တဲ့ဆောင်းပါးတွေကိုလဲ archive လုပ်ထားလို့ရတဲ့အတွက် Link rot ဖြစ်တာတွေ၊ post ဖျက်ခံရတာတွေကနေကာကွယ်ပေးတာပေါ့။ SingleFile extension နဲ့လည်းတွဲသုံးလို့ရတယ်ဆိုတော့ browser မှာ extension သပ်သပ်ထည့်စရာမလိုတော့ဘူးပေါ့။ Google ရဲ့ Gemini Flash model နဲ့ Embedding ကိုလဲထည့်သုံးမယ်ဆိုရင် auto tagging ပါပါတဲ့အတွက် ကိုယ်သိမ်းထားတာများနေရင်လည်း ရှာဖွေရလွယ်ကူစေပါတယ်။

Karakeep ကို self host လုပ်ဖို့ဆိုရင်တော့ container သုံးခုလိုပါတယ်။

  1. အရင်ဆုံး user Quadlet folder လုပ်ပါမယ်။
  • mkdir -p ~/.config/containers/systemd
  1. နောက် အသစ်လုပ်လိုက်တဲ့ folder ထဲမှာ Quadlet ရဲ့ .container file တွေလုပ်ပါမယ်။
  • cd ~/.config/containers/systemd
  • touch {chrome,karakeep,meilisearch}.container
  1. Text editor သုံးပြီး .container file တွေကို edit လုပ်ပါမယ်။

karakeep.container

[Unit]
Requires=nginx.service chrome.service meilisearch.service
After=chrome.service meilisearch.service
Before=nginx.service

[Container]
ContainerName=karakeep
Image=ghcr.io/karakeep-app/karakeep:release
AutoUpdate=registry
EnvironmentFile=%h/server/.env
Environment=MEILI_ADDR=http://karakeep-meilisearch:7700 BROWSER_WEB_URL=http://karakeep-chrome:9222 DATA_DIR=/data
Volume=karakeep_data:/data
Network=kara-ray_default

[Service]
Restart=unless-stopped

[Install]
WantedBy=default.target

chrome.container

[Container]
ContainerName=karakeep-chrome
Image=ghcr.io/karakeep-app/karakeep-chrome:release
Exec=--disable-gpu --disable-dev-shm-usage --hide-scrollbars --disable-blink-features=AutomationControlled --window-size=1440,900
Network=kara-ray_default
DropCapability=all
NoNewPrivileges=true

[Service]
Restart=unless-stopped

meilisearch.container

[Container]
ContainerName=karakeep-meilisearch
Environment=MEILI_NO_ANALYTICS=true
EnvironmentFile=%h/server/.env
Image=docker.io/getmeili/meilisearch:v1.41.0
AutoUpdate=registry
Volume=karakeep_meilisearch:/meili_data
Network=kara-ray_default

NoNewPrivileges=true
ReadOnly=true
DropCapability=all

[Service]
Restart=unless-stopped
  1. နောက်ပြီးတော့ Karakeep လိုအပ်တဲ့ environment file ကို $HOME/server မှာလုပ်ပါမယ်။
  • cd ~
  • mkdir ~/server
  • touch ~/server/.env
  1. .env file မှာလိုအပ်တဲ့အရာတွေဖြည့်ရပါမယ်။ ဘာတွေဖြည့်ရမှာလည်းဆိုတာတော့ Karakeep doc မှာကြည့်ပါ။ ကိုယ့်အတွက်တော့ ဒီ env file တွေကအဆင်ပြေပါတယ်။

.env

PORT=3000
KARAKEEP_VERSION=release
NEXTAUTH_SECRET=
MEILI_MASTER_KEY=
NEXTAUTH_URL=
MAX_ASSET_SIZE_MB=100
DISABLE_SIGNUPS=true
OPENAI_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
OPENAI_API_KEY=
INFERENCE_TEXT_MODEL=gemini-3.6-flash
EMBEDDING_TEXT_MODEL=gemini-embedding-2
EMBEDDING_DIMENSIONS=3072
EMBEDDING_ENABLE_AUTO_INDEXING=true
CRAWLER_SCREENSHOT_TIMEOUT_SEC=30
CRAWLER_JOB_TIMEOUT_SEC=180
CRAWLER_FULL_PAGE_SCREENSHOT=true
  1. Karakeep ကိုစလို့ရပါပြီ။
  • systemctl --user daemon-reload
  • systemctl --user start karakeep လုပ်လိုက်ရင်လိုအပ်တဲ့ Image တွေ download ဆွဲပြီး container တွေစပါပြီ။ systemctl --user status karakeep သုံးပြီးစစ်လို့ရပါတယ်။

GoToSocial

GoToSocial (Gts) ကတော့ Mastodon လိုမျိုး Fediverse ထဲက social media ပါ။ Light weight လည်းဖြစ် setup လုပ်ရတာလည်းရှင်းတယ်ဆိုလို့ စမ်းလုပ်ကြည့်ထားတာပါ။ Light weight ဖြစ်တာနဲ့အညီ web ဗားရှင်းက ဒီတိုင်းသုံးလို့မရပါဘူး။ Application တစ်ခုခုလိုပါမယ်။ ကိုယ့်ကတော့ Phanpy မှာ login ဝင်ပြီးသုံးပါတယ်။ ဆာဗာထောင်ဖို့ကတော့ container တစ်ခုထဲပဲလိုပါတယ်။ အပေါ်က quadlet folder ထဲမှာ gts.container file လုပ်ပါမယ်။

gts.container

[Unit]
Requires=nginx.service
Before=nginx.service

[Container]
ContainerName=gts
Image=docker.io/superseriousbusiness/gotosocial:latest
AutoUpdate=registry
Volume=gts_data:/gotosocial/storage
Volume=%h/server/gts:/config:ro
UserNS=keep-id:uid=1000,gid=1000
Environment=GTS_CONFIG_PATH=/config/config.yaml
Network=kara-ray_default

[Service]
Restart=unless-stopped

[Install]
WantedBy=default.target

Config file ကတော့ $HOME/server/gts folder ထဲမှာထည့်ပါ။ Config file ထဲဘာထည့်ရမလဲဆိုတာကတော့ Gts docs ကိုဖတ်ပါ။ ဖြည့်ပြီးရင်တော့ systemctl --user daemon-reload နဲ့ systemctl --user start gts နဲ့စလို့ရပါပြီ။


v2ray

ဒါကတော့ proxy တစ်ခုပါ။ တရုတ်မှာသုံးဖို့ အထိကလုပ်ထားတာပါ။ Setup လုပ်ရတာကလွယ်ကူပေမယ့် ကိုယ့် ဆာဗာ address firewall နဲ့မညှိဖို့ကတော့ နည်းနည်းလုပ်ယူရပါတယ်။ ဒါကြောင့်မို့ WebSocket+TLS+Web နည်းနဲ့ Cloudflare ကိုရှေ့မှာခံသုံးပါတယ်။ Setup လုပ်ဖို့ကတော့ quadlet folder ထဲမှာပဲ

v2ray.container

[Unit]
Requires=nginx.service
Before=nginx.service

[Container]
ContainerName=v2ray
Environment=v2ray.vmess.aead.forced=false VIRTUAL_HOST= VIRTUAL_PORT=1310
Exec=run -c /etc/v2ray/config.json
Image=ghcr.io/v2fly/v2ray:latest
AutoUpdate=registry
Volume=%h/server/v2ray:/etc/v2ray/
Volume=%h/server/v2ray/logs:/var/log/v2ray
Network=kara-ray_default

[Service]
Restart=unless-stopped

[Install]
WantedBy=default.target

Config file ကတော့ ထုံးစံအတိုင်း $HOME/server/v2ray ထဲမှာထည့်ပါမယ်။ mkdir -p ~/server/v2ray/logs နဲ့ touch ~/server/v2ray/config.json။ ဘာထည့်ရမလဲဆိုတာကတော့ docsကြည့်ပါ။ ပြီးရင် systemctl --user daemon-reload လုပ်ပြီး systemctl --user start v2ray နဲ့စလို့ရပါပြီ။


Nginx

အပေါ်က ဆာဗာတွေအကုန်လုံက အခုထိ localhost ပေါ်မှာပဲရှိနေသေးပါတယ်။ အင်တာနက်ပေါ်ရောက်ဖို့ဆိုရင် reverse proxy တစ်ခုခုသုံးဖို့လိုပါမယ်။ ကိုယ်ကတော့ Nginx သုံးပါတယ်။

  1. Quadlet folder ထဲမှာ

nginx.container

[Unit]
Requires=karakeep.service v2ray.service gts.service
After=karakeep.service v2ray.service gts.service

[Container]
ContainerName=nginx
Image=docker.io/nginxinc/nginx-unprivileged:stable-alpine-slim
AutoUpdate=registry
PublishPort=8080:8080
PublishPort=4430:4430
Volume=%h/server/nginx:/etc/nginx/conf.d:ro
Volume=%h/server/certs:/etc/nginx/certs:ro
Network=kara-ray_default

NoNewPrivileges=true
DropCapability=all
ReadOnly=true

[Service]
Restart=unless-stopped

[Install]
WantedBy=default.target
  1. ပြီးရင် config နဲ့ certificate အတွက် mkdir -p ~/server/{nginx,certs}လုပ်ပါမယ်။

~/server/nginx ထဲမှာတော့ အပေါ်က software တွေနဲ့ port တွေကို config လုပ်ပါမယ်။

karakeep.conf

server {
  listen 4430 ssl;
  listen 4430 quic reuseport;
  http2 on; http3 on;
  quic_retry on;
  client_max_body_size 100M;

  ssl_certificate       /etc/nginx/certs/fullchain.pem;
  ssl_certificate_key   /etc/nginx/certs/privkey.key;
  ssl_dhparam /etc/nginx/certs/ssl-dhparams.pem;
  ssl_client_certificate /etc/nginx/certs/cloudflare.crt;
  ssl_verify_client on;
  ssl_session_timeout 10m;
  ssl_session_cache shared:MozSSL:10m;
  ssl_session_tickets off;

  ssl_protocols         TLSv1.3;
  ssl_ciphers           ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_prefer_server_ciphers on;
  ## ssl_certificate_compression on;
  ssl_early_data on;
  server_name            test.com;

  location / {
    proxy_redirect off;
    proxy_pass http://karakeep:3000;
    proxy_http_version 1.1;
    add_header Alt-Svc 'h3=":$server_port"; ma=86400';
    add_header x-quic 'h3';
    add_header       X-Served-By $host;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP          $remote_addr;
  }
}

gts.conf

server {
  listen 4430 ssl;
  http2 on; http3 on;
  quic_retry on;
  client_max_body_size 20M;

  ssl_certificate       /etc/nginx/certs/fullchain.pem;
  ssl_certificate_key   /etc/nginx/certs/privkey.key;
  ssl_dhparam /etc/nginx/certs/ssl-dhparams.pem;
  ssl_client_certificate /etc/nginx/certs/cloudflare.crt;
  ssl_verify_client on;
  ssl_session_timeout 10m;
  ssl_session_cache shared:MozSSL:10m;
  ssl_session_tickets off;

  ssl_protocols         TLSv1.3;
  ssl_ciphers           ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_prefer_server_ciphers on;
  ## ssl_certificate_compression on;
  ssl_early_data on;
  server_name            gts.test.com;

  location / {
    proxy_redirect off;
    proxy_pass http://gts:4040;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
 }
}

v2ray.conf

server {
    server_name v2ray.test.com;

    listen 4430 ssl;
    listen 4430 quic;
    http2 on; http3 on;
    quic_retry on;
    ssl_protocols TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;
    ## ssl_certificate_compression on;
    ssl_early_data on;
    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.key;
    ssl_dhparam         /etc/nginx/certs/ssl-dhparams.pem;
    ssl_client_certificate /etc/nginx/certs/cloudflare.crt;
    ssl_verify_client on;
    ssl_session_timeout 10m;
    ssl_session_cache shared:MozSSL:10m;
    ssl_session_tickets off;

    add_header Alt-Svc 'h3=":$server_port"; ma=86400';
    add_header x-quic 'h3';
    add_header Strict-Transport-Security "max-age=31536000" always;
    client_max_body_size 20M;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    
    location / {
		proxy_pass http://v2ray:1310;
		proxy_http_version 1.1;
		proxy_redirect off;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header Early-Data $ssl_early_data;
	}
}
  1. z.com ကနေ domain ဝယ်ပြီး Cloudflare နဲ့သွားချိတ်ပါမယ်။ ချိတ်နည်းကတော့ docs ကြည့်ပါ။
  2. ပြီးရင် Cloudflare ကနေ origin server TLS certificate ထုတ်ပြီး $HOME/server/certsမှာ fullchain.pem နဲ့ privkey.key ထည့်ပါ။ cloudflare.crt ကိုတစ်နေရာထဲမှာတွေ့နိုင်ပါတယ်။ ဒေါင်းလုဒ်ဆွဲပြီးသိမ်းပါ။
  3. openssl dhparam -out ssl-dhparams.pem 4096 OpenSSL နဲ့ key generate လုပ်ပါ။ မိနစ်ပိုင်းလောက်ကြာနိုင်ပါတယ်။
  4. systemctl --user daemon-reload လုပ်ပြီး systemctl --user start nginx လုပ်လိုက်ရင် nginx container စပါပြီ။ podman exec nginx nginx -t ကိုယ့်ရဲ့ nginx config မှန်လားစစ်လို့ရပါတယ်။

နောက်ဆုံးပိတ်အနေနဲ့ Cloudflare ကနေ ကိုယ့်ဆာဗာကိုချိတ်လို့ရအောင် port ဖွင့်ဖို့လိုပါမယ်။ Unprivileged nginx သုံးတာမို့လို့ port 8080 နဲ့ 4430 လိုပါမယ်။ Arch Linux ပေါ်မှာ firewall အနေနဲ့ ufw သုံးတာမို့လို့ sudo ufw allow 4430 comment nginx နဲ့ sudo ufw allow 8080 comment nginx လုပ်လိုက်ပါ။ Cloudflare ဘက်မှာတော့ origin server ကို port 4430 မှာသွားရှာဖို့ပြောရပါမယ်။ လုပ်နည်း docs ကြည့်ပါ။

အခုဆိုရင်တော့ ကိုယ့်ရဲ့ domain ပေါ်ကနေ ကိုယ်ထောင်ထားတဲ့ software တွေကိုသုံးလို့ရပါပြီ။ ကုန်ကျစရိတ်ကတော့ domain ဖိုးနဲ့ VPS ဖိုးရှိပါမယ်။ ကိုယ့်အိမ်မှာမသုံးဘဲထားထားတဲ့ ဖုန်း၊ ကွန်ပြူတာတွေရှိရင်တော့ VPS ဖိုးသက်သာနိုင်ပါတယ်။

Backup plan အနေနဲ့ကတော့ Backblaze ရဲ့ free 10GB သုံးပါတယ်။ rclone နဲ့ပဲ podman volume ကနေပြတဲ့ directory ကို backup လုပ်လိုက်ပါတယ်။